/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: TOUCH driver
 *
 * Change Logs:
 * Date           Author            Notes
 * 2023-1-4       planck           Init
 */

#include <nxos.h>

#include <drivers/touch.h>
#include <drivers/iic.h>
#include <drivers/gpio.h>
#include <drivers/pin.h>
#include <regs.h>
#include <base/log.h>
#include <base/driver.h>
#include <base/string.h>
#include <base/ioremap.h>
#include <base/error.h>
#include <base/memory.h>
#include <base/clock.h>
#include <base/malloc.h>

/* hardware section */
static NX_U8 GT911_CFG_TBL[] =
    {
        0x6b,
        0x00,
        0x04,
        0x58,
        0x02,
        0x05,
        0x0d,
        0x00,
        0x01,
        0x0f,
        0x28,
        0x0f,
        0x50,
        0x32,
        0x03,
        0x05,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x8a,
        0x2a,
        0x0c,
        0x45,
        0x47,
        0x0c,
        0x08,
        0x00,
        0x00,
        0x00,
        0x40,
        0x03,
        0x2c,
        0x00,
        0x01,
        0x00,
        0x00,
        0x00,
        0x03,
        0x64,
        0x32,
        0x00,
        0x00,
        0x00,
        0x28,
        0x64,
        0x94,
        0xd5,
        0x02,
        0x07,
        0x00,
        0x00,
        0x04,
        0x95,
        0x2c,
        0x00,
        0x8b,
        0x34,
        0x00,
        0x82,
        0x3f,
        0x00,
        0x7d,
        0x4c,
        0x00,
        0x7a,
        0x5b,
        0x00,
        0x7a,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x18,
        0x16,
        0x14,
        0x12,
        0x10,
        0x0e,
        0x0c,
        0x0a,
        0x08,
        0x06,
        0x04,
        0x02,
        0xff,
        0xff,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x16,
        0x18,
        0x1c,
        0x1d,
        0x1e,
        0x1f,
        0x20,
        0x21,
        0x22,
        0x24,
        0x13,
        0x12,
        0x10,
        0x0f,
        0x0a,
        0x08,
        0x06,
        0x04,
        0x02,
        0x00,
        0xff,
        0xff,
        0xff,
        0xff,
        0xff,
        0xff,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x79,
        0x01,
};

static NX_Error gt911_read_regs(NX_U32 addr, NX_U16 *reg, NX_U16 *data, NX_U16 len)
{
    struct i2c_msg msgs[2];

    msgs[0].addr = addr;
    msgs[0].flags = I2C_WR;
    msgs[0].buf = reg;
    msgs[0].len = GT911_REGITER_LEN;

    msgs[1].addr = addr;
    msgs[1].flags = I2C_RD;
    msgs[1].buf = data;
    msgs[1].len = len;

    if (f133_i2c_mst_xfer(&msgs, 2) == 2)
    {
        return NX_EOK;
    }
    else
    {
        return -NX_ERROR;
    }
}

static NX_Error gt911_write_reg(NX_U32 addr, NX_U8 *data, NX_U8 len)
{
    struct i2c_msg msgs;

    msgs.addr = addr;
    msgs.flags = I2C_WR;
    msgs.buf = data;
    msgs.len = len;

    if (f133_i2c_mst_xfer(&msgs, 1) == 1)
    {
        return NX_EOK;
    }
    else
    {
        return -NX_ERROR;
    }
}

static NX_Error gt911_soft_reset(NX_U32 addr)
{
    NX_U8 buf[3];

    buf[0] = (NX_U8)(GT911_COMMAND_REG >> 8);
    buf[1] = (NX_U8)(GT911_COMMAND_REG & 0xFF);
    buf[2] = 0x02;

    if (gt911_write_reg(addr, buf, 3) != NX_EOK)
    {
        NX_LOG_E("soft reset failed");
        return -NX_ERROR;
    }
    return NX_EOK;
}

static NX_I16 pre_x[GT911_MAX_TOUCH] = {-1, -1, -1, -1, -1};
static NX_I16 pre_y[GT911_MAX_TOUCH] = {-1, -1, -1, -1, -1};
static NX_I16 pre_w[GT911_MAX_TOUCH] = {-1, -1, -1, -1, -1};
static NX_U16 s_tp_dowm[GT911_MAX_TOUCH];
static struct touch_data *read_data;

static void gt911_touch_up(void *buf, NX_I8 id)
{
    read_data = (struct touch_data *)buf;

    if (s_tp_dowm[id] == 1)
    {
        s_tp_dowm[id] = 0;
        read_data[id].event = TOUCH_EVENT_UP;
    }
    else
    {
        read_data[id].event = TOUCH_EVENT_NONE;
    }

    read_data[id].timestamp = NX_ClockTickGet();
    read_data[id].width = pre_w[id];
    read_data[id].x_coordinate = pre_x[id];
    read_data[id].y_coordinate = pre_y[id];
    read_data[id].track_id = id;

    pre_x[id] = -1; /* last point is none */
    pre_y[id] = -1;
    pre_w[id] = -1;
}

static void gt911_touch_down(void *buf, NX_I8 id, NX_I16 x, NX_I16 y, NX_I16 w)
{
    read_data = (struct touch_data *)buf;

    if (s_tp_dowm[id] == 1)
    {
        read_data[id].event = TOUCH_EVENT_MOVE;
    }
    else
    {
        read_data[id].event = TOUCH_EVENT_DOWN;
        s_tp_dowm[id] = 1;
    }

    read_data[id].timestamp = NX_ClockTickGet();
    read_data[id].width = w;
    read_data[id].x_coordinate = x;
    read_data[id].y_coordinate = y;
    read_data[id].track_id = id;

    pre_x[id] = x; /* save last point */
    pre_y[id] = y;
    pre_w[id] = w;
}

static NX_IArch gt911_read_point(void *buf, NX_IArch read_num)
{
    NX_U8 point_status = 0;
    NX_U8 touch_num = 0;
    NX_U8 write_buf[3];
    NX_U8 cmd[2];
    NX_U8 read_buf[8 * GT911_MAX_TOUCH] = {0};
    NX_U8 read_index;
    NX_I8 read_id = 0;
    NX_I16 input_x = 0;
    NX_I16 input_y = 0;
    NX_I16 input_w = 0;

    static NX_U8 pre_touch = 0;
    static NX_I8 pre_id[GT911_MAX_TOUCH] = {0};

    /* point status register */
    cmd[0] = (NX_U8)((GT911_READ_STATUS >> 8) & 0xFF);
    cmd[1] = (NX_U8)(GT911_READ_STATUS & 0xFF);

    if (gt911_read_regs(GT911_ADDRESS_HIGH, cmd, &point_status, 1) != NX_EOK)
    {
        read_num = 0;
        goto exit_;
    }

    if (point_status == 0) /* no data */
    {
        read_num = 0;
        goto exit_;
    }

    if ((point_status & 0x80) == 0) /* data is not ready */
    {
        read_num = 0;
        goto exit_;
    }

    touch_num = point_status & 0x0f; /* get point num */

    if (touch_num > GT911_MAX_TOUCH) /* point num is not correct */
    {
        read_num = 0;
        goto exit_;
    }

    cmd[0] = (NX_U8)((GT911_POINT1_REG >> 8) & 0xFF);
    cmd[1] = (NX_U8)(GT911_POINT1_REG & 0xFF);

    /* read point num is touch_num */
    if (gt911_read_regs(GT911_ADDRESS_HIGH, cmd, read_buf, read_num * GT911_POINT_INFO_NUM) != NX_EOK)
    {
        NX_Printf("read point failed\n");
        read_num = 0;
        goto exit_;
    }

    if (pre_touch > touch_num) /* point up */
    {
        for (read_index = 0; read_index < pre_touch; read_index++)
        {
            NX_U8 j;

            for (j = 0; j < touch_num; j++) /* this time touch num */
            {
                read_id = read_buf[j * 8] & 0x0F;

                if (pre_id[read_index] == read_id) /* this id is not free */
                {
                    break;
                }

                if (j >= touch_num - 1)
                {
                    NX_U8 up_id;
                    up_id = pre_id[read_index];
                    gt911_touch_up(buf, up_id);
                }
            }
        }
    }

    if (touch_num) /* point down */
    {
        NX_U8 off_set;

        for (read_index = 0; read_index < touch_num; read_index++)
        {
            off_set = read_index * 8;
            read_id = read_buf[off_set] & 0x0f;
            pre_id[read_index] = read_id;
            input_x = read_buf[off_set + 1] | (read_buf[off_set + 2] << 8); /* x */
            input_y = read_buf[off_set + 3] | (read_buf[off_set + 4] << 8); /* y */
            input_w = read_buf[off_set + 5] | (read_buf[off_set + 6] << 8); /* size */

            gt911_touch_down(buf, read_id, input_x, input_y, input_w);
        }
    }
    else if (pre_touch)
    {
        for (read_index = 0; read_index < pre_touch; read_index++)
        {
            gt911_touch_up(buf, pre_id[read_index]);
        }
    }

    pre_touch = touch_num;

exit_:
    write_buf[0] = (NX_U8)((GT911_READ_STATUS >> 8) & 0xFF);
    write_buf[1] = (NX_U8)(GT911_READ_STATUS & 0xFF);
    write_buf[2] = 0x00;
    gt911_write_reg(GT911_ADDRESS_HIGH, write_buf, 3);

    return read_num;
}

void NX_TouchDriverInit(void)
{
    d1_set_gpio_mode(GPIO_PORT_B, GPIO_PIN_2, OUTPUT); // rst
    d1_set_gpio_mode(GPIO_PORT_B, GPIO_PIN_3, OUTPUT); // int

    d1_set_gpio_val(GPIO_PORT_B, GPIO_PIN_3, 0);
    d1_set_gpio_val(GPIO_PORT_B, GPIO_PIN_2, 0);
    // NX_ClockTickDelayMillisecond(100);

    d1_set_gpio_val(GPIO_PORT_B, GPIO_PIN_2, 1);

    gt911_soft_reset(GT911_ADDRESS_HIGH);
    NX_U8 config[512];

    config[0] = (NX_U8)((GT911_CONFIG_REG >> 8) & 0xFF);
    config[1] = (NX_U8)(GT911_CONFIG_REG & 0xFF);
    NX_MemCopy(&config[2], GT911_CFG_TBL, sizeof(GT911_CFG_TBL));

    // NX_ClockTickDelayMillisecond(10);

    gt911_write_reg(GT911_ADDRESS_HIGH, config, sizeof(GT911_CFG_TBL) + GT911_ADDR_LEN);
}

static struct touch_data *read_data_t;
NX_PRIVATE void NX_TouchDriverThread(void *arg)
{
    read_data_t = (struct read_data *)NX_MemAlloc(sizeof(struct touch_data) * 2);

    while (1)
    {

        gt911_read_point(read_data_t, 2);
        NX_Printf("touch bar test\n");
        for (int i = 0; i < 2; i++)
        {
            if (read_data_t[i].event == TOUCH_EVENT_DOWN || read_data_t[i].event == TOUCH_EVENT_MOVE)
            {
                NX_Printf("%d %d %d %d %d\n", read_data_t[i].track_id,
                          read_data_t[i].x_coordinate,
                          read_data_t[i].y_coordinate,
                          read_data_t[i].timestamp,
                          read_data_t[i].width);
            }
        }
        NX_ThreadSleep(10);
    }
}
